﻿using OpenCvSharp;
using OpenCvSharp.ML;
using System;
using System.Collections.Generic;

namespace OpenCvSharpExtension
{

    public static class HogHelper
    {
        /// <summary>
        /// 提取特征
        /// </summary> 
        /// <param name="sampleSize">样本大小</param>
        /// <param name="posFiles">正样本文件</param>
        /// <param name="diFiles">负样本文件</param>
        public static SVM SVMTrain(Size sampleSize, string[] posFiles, string[] diFiles)
        {
            var blockSize = new Size(16, 16);  //new Size(sampleSize.Width / 3, sampleSize.Height / 3);
            var blockStride = new Size(8, 8); //new Size(blockSize.Width / 2, blockSize.Height / 2);
            var cellSize = new Size(8, 8); //new Size(blockSize.Width / 2, blockSize.Height / 2);//4个cell构成一个block
            var channel = 9;
            HOGDescriptor hog = new HOGDescriptor(sampleSize, blockSize, blockStride, cellSize, channel);

            ///计算样本纬度
            var wd = ((sampleSize.Width - blockSize.Width) / blockStride.Width + 1) * ((sampleSize.Height - blockSize.Height) / blockStride.Height + 1) * (blockSize.Width / cellSize.Width) * (blockSize.Height / cellSize.Height) * channel;

            var posCount = posFiles == null ? 0 : posFiles.Length;
            var diCount = diFiles == null ? 0 : diFiles.Length;


            List<float> dataArray = new List<float>(wd * (posCount + diCount));
            List<int> desArray = new List<int>(posCount + diCount);
            //提取正样本信息
            for (int i = 0; i < posCount; i++)
            {
                Mat img = new Mat(posFiles[i], ImreadModes.Grayscale);
                img = img.Resize(sampleSize);//重设样本文件的尺寸 
                var fArray = hog.Compute(img);
                dataArray.AddRange(fArray);
                desArray.Add(1);
            }
            //提取负样本信息
            for (int i = 0; i < diCount; i++)
            {
                Mat img = new Mat(diFiles[i], ImreadModes.Grayscale);
                img = img.Resize(sampleSize);
                var fArray = hog.Compute(img);
                dataArray.AddRange(fArray);
                desArray.Add(-1);
            }
            var dataMat = new Mat(posCount + diCount, wd, MatType.CV_32FC1, dataArray.ToArray());
            var desMat = new Mat(posCount + diCount, 1, MatType.CV_32SC1, desArray.ToArray());
            //训练SVM
            var svm = SVM.Create();

            svm.Type = SVM.Types.CSvc;
            svm.KernelType = SVM.KernelTypes.Linear;
            svm.TermCriteria = TermCriteria.Both(1000, 0.001);//1000次或者收敛达到0.001就跳出
            svm.Degree = 100.0;
            svm.Gamma = 100.0;
            svm.Coef0 = 1.0;
            svm.C = 1.0;
            svm.Nu = 0.5;
            svm.P = 0.1;
            svm.Train(dataMat, SampleTypes.RowSample, desMat);

            return svm;
        }


        public static float[] GetSupportVector(this SVM svm)
        {
            var vectors = svm.GetSupportVectors();

            var row0 = vectors.Row(0);
            float[] vector;
            row0.GetArray(out vector);
            return vector;
        }

        /// <summary>
        /// 识别特征
        /// </summary>
        /// <param name="img"></param>
        /// <param name="svm"></param>
        public static Rect[] Detect(Mat img, float[] vector, Size sampleSize, out double[] foundWeights)
        {
            var blockSize = new Size(16, 16);  //new Size(sampleSize.Width / 3, sampleSize.Height / 3);
            var blockStride = new Size(8, 8); //new Size(blockSize.Width / 2, blockSize.Height / 2);
            var cellSize = new Size(8, 8); //new Size(blockSize.Width / 2, blockSize.Height / 2);//4个cell构成一个block
            var channel = 9;
            HOGDescriptor hog = new HOGDescriptor(sampleSize, blockSize, blockStride, cellSize, channel);

            hog.SetSVMDetector(vector);
            var check = hog.CheckDetectorSize();
            if (check)
            {

            }
            else
            {
                throw new Exception("CheckDetectorSize ERROR.");
            }
            Rect[] found = hog.DetectMultiScale(img, out foundWeights, 1, new Size(8, 8), new Size(16, 16), 1.05, 2);
            return found;
            //foreach (Rect rect in found)
            //{
            //    // the HOG detector returns slightly larger rectangles than the real objects.
            //    // so we slightly shrink the rectangles to get a nicer output.
            //    var r = new Rect
            //    {
            //        X = rect.X + (int)Math.Round(rect.Width * 0.1),
            //        Y = rect.Y + (int)Math.Round(rect.Height * 0.1),
            //        Width = (int)Math.Round(rect.Width * 0.8),
            //        Height = (int)Math.Round(rect.Height * 0.8)
            //    };
            //    img.Rectangle(r.TopLeft, r.BottomRight, Scalar.Red, 3);
            //}

            //using (var window = new Window("people detector", WindowMode.Normal, img))
            //{
            //    window.SetProperty(WindowProperty.Fullscreen, 1);
            //    Cv2.WaitKey(0);
            //}
        }
    }

}
